#pragma once
#include <xercesc/dom/DOM.hpp>
#include <xercesc/dom/DOMErrorHandler.hpp>

XERCES_CPP_NAMESPACE_USE

namespace xml
{
	typedef xercesc::DOMDocument*		IXMLDOMDocumentPtr;
	typedef xercesc::DOMNode*			IXMLDOMNodePtr;
	typedef xercesc::DOMNodeList*		IXMLDOMNodeListPtr;
	typedef xercesc::DOMElement*		IXMLDOMElementPtr;
	typedef xercesc::DOMAttr*			IXMLDOMAttributePtr;
	typedef xercesc::DOMText*			IXMLDOMTextPtr;
	typedef xercesc::DOMNamedNodeMap*	IXMLDOMNamedNodeMapPtr;

	class XmlLibInit
	{
	public:
		XmlLibInit();
		~XmlLibInit();
	};
	typedef boost::shared_ptr<XmlLibInit> PXmlLibInit;
	
	template<class NATIVE_CH,class EXT_CH> 
	class HC_API XStr
	{
	public :
		XStr(const EXT_CH* toTranscode) 
		{
			_fNativeForm = XMLString::transcode(toTranscode);
		}
		~XStr()	
		{
			XMLString::release(&_fNativeForm);	
		}
		NATIVE_CH* nativeForm() 
		{
			return _fNativeForm;
		}
	private :
		NATIVE_CH*   _fNativeForm;
	};
	#define X(c) 	XStr<XMLCh,char>(c).nativeForm()
	#define X2C(wc) XStr<char,XMLCh>(wc).nativeForm() 


	class node
	{
	public:
		node( const std::string& iname="" ): type(), name( iname ) 
		{}
		node( xercesc::DOMNode::NodeType itype, const std::string& iname ): type( itype ), name( iname ) 
		{}
	
		xercesc::DOMNode::NodeType	type;
		std::string					name;
	};

	class nodelist
	{
	public:
		nodelist( const std::string& iname ): name( iname ) 
		{}
		std::string	name;
	};

	class element: public node
	{
	public:
		element( const std::string& iname ): node( xercesc::DOMNode::ELEMENT_NODE, iname ) 
		{}
	};

	class comment: public node
	{
	public:
		comment(): node( xercesc::DOMNode::COMMENT_NODE, string() ) {}
	};

	class text: public node
	{
	public:
		text(): node( xercesc::DOMNode::TEXT_NODE, string() ) {}
	};

	class attribute: public node
	{
	public:
		attribute( const std::string& iname ): node( xercesc::DOMNode::ATTRIBUTE_NODE, iname ) 
		{}
	};

	class data_type
	{
	public:
		data_type( const char* itype ): type( itype ) 
		{}
		data_type( const std::string& itype ): type( itype ) 
		{}
		const std::string type;
	};

	class pnode;
	class pnodelist;
	class pelement;
	class pattribute;
	class pdocument;
	class pfragment;
	class pinstruction;

	class xmlobject
	{
	public:
		virtual ~xmlobject() {};
		virtual std::string xml_get_typeid() const = 0;
		virtual void xml_save_state( xml::pnode ) const = 0;
	};
	typedef xmlobject*		pxmlobject;
	typedef const xmlobject*	pconstxmlobject;

	class HC_API pnodelist //: private hc::noncopyable
	{
		friend class pnode;
	public:
		pnodelist( const pnodelist& nodelist);

		long	length()	const;
		bool	have_next() const ;
		pnode	nextnode()	const;
//		void	returnToStart() const;

	protected:
		pnodelist( IXMLDOMNodePtr iptr, const std::string& iname );
		IXMLDOMNodePtr				_node_ptr;
		list<IXMLDOMNodePtr>		_nodes_ptr;
		mutable list<IXMLDOMNodePtr>::const_iterator	_nodes_itr;
	
	};

	class HC_API pnode
	{
	public:
		static const data_type		int_type;
		static const data_type		int64_type;
		static const data_type		string_type;
		static const data_type		boolean_type;
		static const data_type		float_type;
		static const data_type		ui4_type;
		static const data_type		datetime_type;
		static const xml::attribute	typeid_attribute;
		static const xml::attribute	msdt_attribute;
		static const xml::attribute	size_attribute;
		static const xml::attribute	index_attribute;
		static const xml::element	item_element;
		static const std::string	empty_string;
		static const std::string	array_type;
		static const std::string	class_event;

		pnode( IXMLDOMNodePtr iptr ):_node_ptr( iptr )
		{}

		pnode clone( bool deep=true ) const;
		bool valid() const { return _node_ptr != NULL; }
		IXMLDOMNodePtr get_node_ptr() const { return _node_ptr; }
		//bool exist_node( const string& i ) const { return (*this >> xml::node( i )).valid(); }
		bool exist_value_in_node(const std::string& i) const; 
	
		pnode 		operator <<( const xml::element& i );
		pnode 		operator <<( const xml::attribute& i );
		pnode& 		operator <<( const pconstxmlobject& i );
		pnode& 		operator <<( const xml::pnode& i );
		pnode& 		operator <<( const std::map<std::string,hc::_value_t>& i );
		pnode& 		operator <<( const hc::_value_t& i );

		template<class T>
		pnode& 		operator <<( const std::list<T>& i );
	
		pnode& 		operator <<( const std::wstring& i) 	{return *this<<hc::_value_t(i);}
		pnode& 		operator <<( const std::string& i ) 	{return *this<<hc::_value_t(i);}
		pnode& 		operator <<( const char* i) 			{return *this<<std::string(i);}

		const pnode		operator >>( const xml::attribute& o ) const;
		const pnode		operator >>( const xml::node& o ) const;
		const pnodelist	operator >>( const xml::nodelist& o ) const;
		const pnode& 	operator >>( std::map<std::string,hc::_value_t>& o ) const;
		const pnode& 	operator >>( hc::_value_t& o ) const;
		template<class T> 
		const pnode&	operator >>( T& o ) const
		{ 
			if(!_node_ptr) return *this;

			if(_node_ptr->getNodeType() == xercesc::DOMNode::ELEMENT_NODE)
			{
				pnode p = (*this)>>xml::node();
				if(p.valid())
					o = p.get<T>();
				//else
				//	throw CException(	ex_val("Fetching empty value",get_name()));
			}
			else
				o = get<T>();
			return *this; 
		}

		std::string		get_name()		const;
		pnodelist		get_children()	const;
		std::wstring 	as_wstring()	const;
		std::string 	as_string()		const;
		int 			as_int()		const;
		unsigned long 	as_ulong()		const;
		bool 			as_bool()		const;
		void 			as_list(hc::_value_t& o_val) const;

		template<class T> T get() const;

	protected:
		pnode(): _node_ptr(NULL) {}
		IXMLDOMNodePtr	_node_ptr;
	};

	template<> std::wstring pnode::get<std::wstring>() const;
	template<> std::string pnode::get<std::string>() const;
	template<> int pnode::get<int>() const;
	template<> unsigned long pnode::get<unsigned long>() const;
	template<> bool pnode::get<bool>() const;

	class HC_API pattribute: public pnode
	{
	public:
		pattribute( IXMLDOMAttributePtr iptr );
		IXMLDOMAttributePtr get_attribute_ptr() const { return _attribute_ptr; }

	protected:
		IXMLDOMAttributePtr	_attribute_ptr;
	};

	class HC_API pelement: public pnode
	{
	public:
		pelement( IXMLDOMElementPtr iptr );
		/*pnode operator <<( const xml::element& i ) { return pnode::operator <<( i ); }
		pattribute operator <<( const xml::attribute& i );
 		pelement& operator <<( const _value_t& i );*/

		IXMLDOMElementPtr get_element_ptr() const { return _element_ptr; }
	
		//pelement clone( bool deep = true ) const;

	protected:
		IXMLDOMElementPtr	_element_ptr;
	};

	class HC_API pdocument: public pnode
	{
	public:
		pdocument();
		explicit pdocument(const std::string& iFile) ;
		~pdocument();

 		pnode operator <<(const xml::element& i);
		pnode operator >>(const xml::element& i) const;

 		//pnode clone( bool deep = true ) const;
		bool load_xml(const std::string& iFile);
		string get_xml() const;
		
		xml::pnode get_root();

	protected:
		IXMLDOMDocumentPtr	_document_ptr;
		bool 				_doc_allocated;

		std::string 	_docid;	
		DOMLSParser* 	_dom_builder;

		PXmlLibInit 	_lib_usecounter;
	};

	class DOMCountErrorHandler : public DOMErrorHandler
	{
	public:    
		DOMCountErrorHandler();
		~DOMCountErrorHandler();

		bool	getSawErrors() const;

		bool	handleError(const DOMError& domError);
		void	resetErrors();
		std::string	getErrorLog();
	private :
		DOMCountErrorHandler(const DOMCountErrorHandler&);
		void	operator=(const DOMCountErrorHandler&);
		bool	fSawErrors;
		std::string	errorLog;
	};
	inline bool DOMCountErrorHandler::getSawErrors() const
	{
		return fSawErrors;
	}
}; // xml namespace
